//use std::process::Stdio;

mod mercury;
mod readability;
mod youtube;
mod article_extractor;

use std::sync::Arc;

//use tokio::process::Command;
//use tokio::time::timeout;
//use wait_timeout::ChildExt;
use url::Url;
// pub async fn parse_old(url: &Url) -> Result<MercuryOutput, MercuryError> {
//     //let relative_path = env::var("MERCURY_PATH").context("Cannot find 'MERCURY_PATH' variable")?;
//     //let full_path = canonicalize(relative_path).context("failed to find path")?;
//     let process_timeout = Duration::from_secs(5);
//     let mut process = Command::new("mercury-parser")
//         .stdout(Stdio::piped())
//         .stderr(Stdio::piped())
//         .arg(&url.to_string())
//         .arg("--format=html")
//         .spawn()?;
//     let mut stdout = Vec::new();
//     let child_stdout = process.stdout.take();
//     tokio::io::copy(&mut child_stdout.unwrap(), &mut stdout).await?;

//     let exit_status = match timeout(process_timeout, process.wait()).await {
//         Ok(n) => n?,
//         Err(_) => {
//             process.kill().await?;
//             return Err(MercuryError::ProcessTimeout);
//         }
//     };
//     if exit_status.success() {
//         let json = stdout.as_slice();
//         Ok(serde_json::from_slice::<MercuryOutput>(json)?)
//     } else {
//         let mut stderr = vec![];
//         if let Some(mut reader) = process.stderr {
//             reader.read_to_end(&mut stderr).await?;
//         }
//         let stderr = String::from_utf8(stderr).unwrap_or_default();
//         Err(MercuryError::OtherError)
//     }
// }

pub async fn parse(url: &Url) -> Result<ParserOutput, ParserError> {
    let url = Arc::new(url.to_owned());
    let mut output = ParserOutput {
        title: None,
        word_count: None,
        duration: None,
    };
    let url_youtube = url.clone();
    // let youtube_output = tokio::spawn(async move {
    //     youtube::youtube(url_youtube).await
    // }).await;
    let url_mercury = url.clone();
    // let mercury_output = tokio::spawn(async move {
    //     mercury::mercury(url_mercury).await
    // }).await;
    let url_readability = url.clone();
    let url_extractor = url.clone();
    //     let extractor_output = tokio::spawn(async move {
    let (youtube_output, mercury_output, readability_output, extractor_output) = tokio::join!(
        youtube::youtube(url_youtube),
        mercury::mercury(url_mercury),
        readability::readability(url_readability),
        article_extractor::article_extractor(url_extractor), 
    );
    match youtube_output {
        Ok(Some(video)) => {
            output.title = Some(video.title.clone());
            if let Some(duration) = video.duration {
                output.duration = duration.as_i64()
            }
        }
        Err(e) => {
            println!("{e:#?}")
        }
        _ => {}
    }
    if let Ok(readability_output) = readability_output {
        if let Some(length) = readability_output.length {
            if output.word_count.is_none() {
                output.word_count = Some(length)
            }
        }
        if let Some(title) = readability_output.title {
            if output.title.is_none() {
                output.title = Some(title)
            }
        }
    }
    if let Ok(extractor_output) = extractor_output {
        if let Some(ttr) = extractor_output.ttr {
            if output.word_count.is_none() {
                output.word_count = Some(ttr*300)
            }
        }
        if let Some(title) = extractor_output.title {
            if output.title.is_none() {
                output.title = Some(title)
    }
            }
        }
    if let Ok(mercury_output) = mercury_output {
        if let Some(title) = mercury_output.title {
            if output.title.is_none() {
                output.title = Some(title)
            }
        }
        if let Some(word_count) = mercury_output.word_count {
            if output.word_count.is_none() && output.duration.is_none() {
                output.word_count = Some(word_count);
            }
        }
    }
    Ok(output)
}
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct ParserOutput {
    pub title: Option<String>,
    pub word_count: Option<i64>,
    pub duration: Option<i64>,
}
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ParserError {
    #[error("Parameter url is invalid")]
    InvalidUrl,
    #[error("failed to start mercury process")]
    CommandError(#[from] std::io::Error),
    #[error("Could not parse stderr")]
    ParseResponse,
    #[error("Process timed out")]
    ProcessTimeout,
    // #[error("cannot deserialize json")]
    // SerdeError(#[from] serde_json::Error),
    #[error("cannot deserialize json")]
    SerdeError(),
    #[error("failed to start mercury process")]
    RequestError(#[from] reqwest::Error),
    #[error("failed to start mercury process")]
    ParserError(#[from] url::ParseError),
    #[error("mercury error: {0}")]
    MercuryError(String),
    #[error("tokio join error")]
    JoinError(#[from] tokio::task::JoinError),
    #[error("some error: {0}")]
    OtherError(String),
}
#[derive(Debug, Deserialize)]
struct MercuryOutputError {
    error: bool,
    messages: String,
}

/*pub async fn parse(url: &Url) -> Result<MercuryOutput, MercuryError> {
    //let relative_path = env::var("MERCURY_PATH").context("Cannot find 'MERCURY_PATH' variable")?;
    //let full_path = canonicalize(relative_path).context("failed to find path")?;
    let _process_timeout = Duration::from_secs(5);
    let _params = &[("url", url.to_string())];
    let request_url = format!("http://mercury:3000/parser?url={}", url.as_str());
    //request_url.set_port(Some(3000)).map_err(|_| println!("set port error!"));
    println!("{:#?}", request_url);
    let output = reqwest::get(request_url).await?.text().await?;
    println!("{:?}", output);

    if let Ok(output) = serde_json::from_str::<MercuryOutput>(&output) {
        println!("{:?}", output);
        return Ok(output);
    }
    if let Ok(output_error) = serde_json::from_str::<MercuryOutputError>(&output) {
        println!("{:?}", output_error);
        return Err(MercuryError::MercuryError(output_error.messages));
    }

    Err(MercuryError::SerdeError())

    // println!("mercury: {:#?}", output);
    // let output = output?;
    //   output
}
*/
